﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Net.FlowField
{
    public class FlowField
    {
        public Vector2Int[,] Directions;
        public int[,] CostField;
        public int Width;
        public int Height;

        public void Initialize(int width, int height)
        {
            Width = width;
            Height = height;
            Directions = new Vector2Int[width, height];
            CostField = new int[width, height];
        }

        /// <summary>
        /// 计算流场
        /// </summary>
        /// <param name="grid">网格数据</param>
        /// <param name="targets">目标点集合</param>
        /// <param name="isRed">是否为红方</param>
        public void CalculateFlowField(in GridCell[,] grid, List<Vector2Int> targets, bool isRed)
        {
            ResetDirections(); // 重置方向矩阵

            if (targets.Count == 0) return;

            CalculateCostField(grid); // 计算成本场
            var integrationField = CalculateIntegrationField(targets); // 计算积分场
            CalculateFlowDirections(integrationField); // 计算流场方向
        }

        /// <summary>
        /// 重置方向矩阵
        /// </summary>
        private void ResetDirections()
        {
            Directions = new Vector2Int[Width, Height];
            CostField = new int[Width, Height];
        }

        /// <summary>
        /// 计算成本场
        /// </summary>
        private void CalculateCostField(GridCell[,] grid)
        {
            var result = Parallel.For(0, Width, x =>
            {
                for (int y = 0; y < Height; y++)
                {
                    CostField[x, y] = grid[x, y].IsObstacleBool ? int.MaxValue : 1;
                }
            });
            while (!result.IsCompleted) ;
        }

        /// <summary>
        /// 计算积分场 (使用Dijkstra算法)
        /// </summary>
        private int[,] CalculateIntegrationField(List<Vector2Int> targets)
        {
            var integrationField = InitializeIntegrationField();
            var openSet = new Queue<Vector2Int>(2048);

            // 初始化目标点
            InitializeTargets(targets, integrationField, openSet);

            // 处理队列
            ProcessIntegrationField(integrationField, openSet);

            return integrationField;
        }

        /// <summary>
        /// 初始化积分场
        /// </summary>
        private int[,] InitializeIntegrationField()
        {
            var field = new int[Width, Height];
            var result = Parallel.For(0, Width, x =>
            {
                for (int y = 0; y < Height; y++)
                {
                    field[x, y] = int.MaxValue;
                }
            });
            while (!result.IsCompleted) ;

            return field;
        }

        /// <summary>
        /// 初始化目标点
        /// </summary>
        private void InitializeTargets(List<Vector2Int> targets, int[,] integrationField, Queue<Vector2Int> openSet)
        {
            foreach (var target in targets)
            {
                integrationField[target.x, target.y] = 0;
                openSet.Enqueue(target);
            }
        }

        /// <summary>
        /// 处理积分场计算
        /// </summary>
        private void ProcessIntegrationField(int[,] integrationField, Queue<Vector2Int> openSet)
        {
            while (openSet.Count > 0)
            {
                var current = openSet.Dequeue();
                int currentCost = integrationField[current.x, current.y];

                // 检查所有相邻格子
                for (int dx = -1; dx <= 1; dx++)
                {
                    for (int dy = -1; dy <= 1; dy++)
                    {
                        if (dx == 0 && dy == 0) continue;

                        ProcessNeighbor(current, dx, dy, currentCost, integrationField, openSet);
                    }
                }
            }
        }

        /// <summary>
        /// 处理相邻格子
        /// </summary>
        private void ProcessNeighbor(Vector2Int current, int dx, int dy, int currentCost, int[,] integrationField, Queue<Vector2Int> openSet)
        {
            int nx = current.x + dx;
            int ny = current.y + dy;

            if (nx >= 0 && nx < Width && ny >= 0 && ny < Height && CostField[nx, ny] != int.MaxValue)
            {
                // 对角线移动成本为14，直线移动成本为10
                int moveCost = (Math.Abs(dx) + Math.Abs(dy) == 2) ? 14 : 10;
                int newCost = currentCost + CostField[nx, ny] * moveCost;

                if (newCost < integrationField[nx, ny])
                {
                    integrationField[nx, ny] = newCost;
                    openSet.Enqueue(new Vector2Int(nx, ny));
                }
            }
        }

        /// <summary>
        /// 计算流场方向
        /// </summary>
        private void CalculateFlowDirections(int[,] integrationField)
        {
            var result = Parallel.For(0, Width, x =>
            {
                for (int y = 0; y < Height; y++)
                {
                    if (integrationField[x, y] == int.MaxValue)
                        continue;
                    FindBestDirection(x, y, integrationField);
                }
            });
            while (!result.IsCompleted) ;
        }

        /// <summary>
        /// 寻找最佳移动方向
        /// </summary>
        private void FindBestDirection(int x, int y, int[,] integrationField)
        {
            int minCost = integrationField[x, y];
            var bestDirection = Vector2Int.zero;

            // 检查所有相邻格子
            for (int dx = -1; dx <= 1; dx++)
            {
                for (int dy = -1; dy <= 1; dy++)
                {
                    if (dx == 0 && dy == 0) continue;

                    int nx = x + dx;
                    int ny = y + dy;

                    if (nx >= 0 && nx < Width && ny >= 0 && ny < Height)
                    {
                        if (integrationField[nx, ny] < minCost)
                        {
                            minCost = integrationField[nx, ny];
                            bestDirection = new Vector2Int(dx, dy);
                        }
                    }
                }
            }

            Directions[x, y] = bestDirection;
        }
    }
}